#include "tts.h"

/*
* note: embeded certificate
* @brief This is the certificate for the TTS API.
*/
extern const char my_tts_cert_pem_start[] asm("_binary_my_tts_cert_pem_start");
extern const char my_tts_cert_pem_end[] asm("_binary_my_tts_cert_pem_end");

char* baidu_aip_token = NULL;       // store the tts token


/*
* @brief TTS class constructor
* @note This function initializes the TTS object.
*/
TTS::TTS() : L() {
    ESP_LOGI(TAG, "Loudspeaker create well");
}


/*
* @brief TTS class destructor
* @note This function is called when the TTS object is destroyed.
*/
TTS::~TTS() {
    ESP_LOGI(TAG, "Loudspeaker destroyed");
}


/*
* @brief Event handler for token retrieval
* @param evt Pointer to the HTTP client event
* @return ESP_OK on success, or an error code on failure
* @note This function handles various events from the HTTP client.
* It processes the received data and parses the JSON response.
* It accumulates data into a static buffer and resets the buffer when the event is finished.
*/
static esp_err_t TokenGet_event_handler(esp_http_client_event_t *evt) {
    static char response_buffer[MAX_TOKEN_RESPONSE_LEN];  // 静态缓冲区存储响应
    static size_t response_len = 0;                 // 当前数据长度

    switch (evt->event_id) {
        case HTTP_EVENT_ON_DATA: {
            // 累积数据到缓冲区
            if (response_len < MAX_TOKEN_RESPONSE_LEN - 1) {
                size_t to_copy = MIN(evt->data_len, MAX_TOKEN_RESPONSE_LEN - response_len - 1);
                memcpy(response_buffer + response_len, evt->data, to_copy);
                response_len += to_copy;
            }
            break;
        }
        case HTTP_EVENT_ON_FINISH: {
            // 确保字符串以结束符结尾
            response_buffer[response_len] = '\0';
            // 解析JSON
            cJSON *root = cJSON_Parse(response_buffer);
            if (root) {
                cJSON *refresh_token = cJSON_GetObjectItem(root, "access_token");
                // refresh_token
                if (cJSON_IsString(refresh_token)) {
                    ESP_LOGI(TAG, "access_token: %s", refresh_token->valuestring);
                    // 释放之前的 token
                    free(baidu_aip_token);
                    // 为新的 token 分配内存
                    baidu_aip_token = strdup(refresh_token->valuestring);
                    // printf("token: %s \r\n", baidu_aip_token);
                } else {
                    ESP_LOGE(TAG, "access_token not found");
                }
                cJSON_Delete(root);
            } else {
                ESP_LOGE(TAG, "JSON解析失败");
            }
            // 重置缓冲区
            response_len = 0;
            break;
        }
        case HTTP_EVENT_DISCONNECTED: {
            response_len = 0;  // 重置以应对重连
            break;
        }
        default:
            break;
    }
    return ESP_OK;
}


/*
* @brief Fetches the TTS token from the server
* @note This function sends an HTTP POST request to the TTS API to retrieve the token.
* It uses the esp_http_client library to handle the HTTP request and response.
* The function is called when the TTS object is in use.
* It initializes the HTTP client with the necessary configuration,
* including the URL, method, and event handler.
* It performs the request and checks for errors.
*/
void TTS::fetch_token_tts() {
    esp_http_client_config_t config = {
        .url = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=" TTS_API_KEY "&client_secret=" TTS_SECRET_KEY,
        .cert_pem = my_tts_cert_pem_start,
        .method = HTTP_METHOD_POST,
        .timeout_ms = 10000,  // 设置超时为10秒
        .event_handler = TokenGet_event_handler,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);
    // esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
    // esp_http_client_set_header(client, "Content-Length", "0");
    esp_err_t err = esp_http_client_perform(client);
    
    if (err == ESP_OK) {
        int status_code = esp_http_client_get_status_code(client);
        ESP_LOGI(TAG, "status_code: %d", status_code);
    } else {
        ESP_LOGE(TAG, "HTTP请求失败: %s", esp_err_to_name(err));
    }

    // 确保每次请求后释放资源
    esp_http_client_cleanup(client);
}


/*
* @brief Event handler for TTS events
* @param evt Pointer to the HTTP client event
* @return ESP_OK on success, or an error code on failure
* @note This function handles various events from the HTTP client.
* It processes the received data and handles connection events.
* It plays the audio data using the Loudspeaker class.
*/
esp_err_t TTS::tts_event_handler(esp_http_client_event_t* evt) {

    TTS* tts = static_cast<TTS*>(evt->user_data); // 获取实例指针
    if (!tts) {
        ESP_LOGI(TAG, "failed to reinterpret_cast");
        return ESP_FAIL;
    }

    static bool is_audio_data = false;  // 标识是否开始接收音频数据
    
    switch (evt->event_id) {
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGI(TAG, "连接服务器成功");
            break;
            
        case HTTP_EVENT_ON_HEADER:
            // 识别音频数据开始（根据百度API特性）
            if (strcasecmp(evt->header_key, "Content-Type") == 0) {
                if (strstr(evt->header_value, "audio/basic") != NULL) {     // audio/basic
                    is_audio_data = true;
                    ESP_LOGI(TAG, "开始接收PCM数据");
                }
            }
            break;

        case HTTP_EVENT_ON_DATA: {
            if (!is_audio_data) {
                // 打印非音频数据（可能是错误信息）
                ESP_LOGW(TAG, "接收非音频数据: %.*s", evt->data_len, (char*)evt->data);
            }
            if (is_audio_data && evt->data_len > 0) {

                tts->L.play(evt->data, evt->data_len);

            }
            break;
        }
        case HTTP_EVENT_ON_FINISH: {
            ESP_LOGI(TAG, "音频传输完成");
            break;
        }
            
        case HTTP_EVENT_DISCONNECTED: {
            ESP_LOGI(TAG, "连接断开");
            is_audio_data = false;
            break;
        }
        default:
            break;
    }
    return ESP_OK;
}




/*
* @brief URL-encodes a string
* @param str Pointer to the string to be encoded
* @return Pointer to the encoded string
* @note This function encodes special characters in the string to be safe for URL transmission.
*/
char* TTS::url_encode(const char* str) {
    const char* hex = "0123456789ABCDEF";
    size_t len = strlen(str);
    char* encoded = (char* )malloc(len * 3 + 1);
    char* p = encoded;
    
    for (size_t i = 0; i < len; i++) {
        if (isalnum((unsigned char)str[i]) || str[i] == '-' || str[i] == '_' 
            || str[i] == '.' || str[i] == '~') {
            *p++ = str[i];
        } else {
            *p++ = '%';
            *p++ = hex[(unsigned char)str[i] >> 4];
            *p++ = hex[(unsigned char)str[i] & 0xF];
        }
    }
    *p = '\0';
    return encoded;
}


/*
* @brief Demonstration process for TTS
* @note This function demonstrates the TTS functionality by sending a sample text to the TTS API.
*/
void TTS::demo_process() {
    fetch_token_tts();

    // 原始中文
    const char* raw_text = "你好我是飞鸟开发的语音助手小伦，很高兴认识你";
    char* final_text = url_encode(raw_text);

    // sprintf的方式
    // 构造payload
    char payload[1024];
    snprintf(payload, sizeof(payload), 
            "tex=%s&tok=%s&cuid=%s&ctp=1&lan=zh&aue=4&spd=8&per=4103&vol=2",
            final_text, 
            baidu_aip_token, 
            CUID);

    esp_http_client_config_t config = {
        .url = TTS_URL,
        .cert_pem = my_tts_cert_pem_start,
        .method = HTTP_METHOD_POST,
        .event_handler = tts_event_handler,
        .user_data = this,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);

    esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");

    esp_http_client_set_post_field(client, payload, strlen(payload));

    ESP_LOGI(TAG, "tts_json_string: %s", payload);

    esp_err_t err = esp_http_client_perform(client);

    if (err == ESP_OK) {
        ESP_LOGI(TAG, "Status: %d", esp_http_client_get_status_code(client));
    }

    free(final_text);

    esp_http_client_cleanup(client);
}


/*
* @brief Main process for TTS
* @note This function processes the TTS functionality by receiving text from the LLM output queue.
* It fetches the TTS token, URL-encodes the text, constructs the payload,
* and sends an HTTP POST request to the TTS API.
* It handles the response and cleans up any allocated resources.
* The function is called when the TTS object is in use.
*/
void TTS::process() {

    char* llm_text = nullptr;
    if (xQueueReceive(llm_output_queue, &llm_text, portMAX_DELAY) != pdTRUE) {
        ESP_LOGE(TAG, "Failed to receive data from llm_output_queue");
        return;
    }
    ESP_LOGI(TAG, "llm_output_queue: %s", llm_text);

    // 检查队列数据是否有效
    if (llm_text == nullptr || strlen(llm_text) == 0) {
        ESP_LOGE(TAG, "Received empty text from llm_output_queue");
        free(llm_text); // 确保释放可能分配的内存
        return;
    }

    fetch_token_tts();

    // URL编码
    char* final_text = url_encode(llm_text);

    
    if (!final_text) {
        ESP_LOGE(TAG, "URL encoding failed");
        return;
    }

    // 计算payload需要的空间（保守估计）
    size_t payload_size = strlen(final_text) + strlen(baidu_aip_token) + strlen(CUID) + 100;
    char* payload = (char*)malloc(payload_size);
    if (!payload) {
        free(final_text);
        ESP_LOGE(TAG, "Payload memory allocation failed");
        return;
    }

    snprintf(payload, payload_size,
            "tex=%s&tok=%s&cuid=%s&ctp=1&lan=zh&aue=4&spd=8&per=4103&vol=2",
            final_text, 
            baidu_aip_token, 
            CUID);

    // HTTP客户端配置
    esp_http_client_config_t config = {
        .url = TTS_URL,
        .cert_pem = my_tts_cert_pem_start,
        .method = HTTP_METHOD_POST,
        .timeout_ms = 20000,
        .event_handler = tts_event_handler,
        .user_data = this,  // HTTP客户端的事件回调函数 tts_event_handler 需要访问 TTS 类的成员
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
    esp_http_client_set_post_field(client, payload, strlen(payload));

    ESP_LOGI(TAG, "TTS payload: %s", payload);

    // 执行请求
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "TTS Status: %d", esp_http_client_get_status_code(client));
    } else {
        ESP_LOGE(TAG, "HTTP request failed: %d", err);
    }

    // 清理资源
    free(llm_text); // 释放队列数据内存
    free(final_text);
    free(payload);
    esp_http_client_cleanup(client);
}
